/*
 * task manager
 * �û��շ���Ϣͨ��Task���?��������Ϣʹ��cmd���?ͨ��Է������ͷ���з������жϵ�ǰ����ݷ�����ͼ����������Ӧ��
 * ׼����
 * Ŀǰ֧�ֵ����û��򿪸�Ӧ������һ��taskΪlogin����server�߳����������Ӧ�˿ڣ����ӵ��Է�cmdΪlogin����Ϣʱ��
 * ����䵽����list,��ͬʱ����client�߳�������㲥��ַ�����Լ�����Ϣ��ȷ���Է����������Լ���
 * �ṩ�������Ը�UI�㣬�û��޸�socket�������Ϣ��
 * tasktype:��������
 * messageContent:��Ϣ����
 * targetAddress:Ŀ���ַ
 * ��.��ֵ�����ܿ��ǡ�
 */
package com.fantasy;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;

import android.content.Context;
import android.util.Log;

import com.fantasy.util.IMUtil;

public class TaskController {

	private static final String TAG = "TaskController";
	private static final boolean DEBUG = true;

	private static final int TIMEOUT = 10000;
	private static final int MAXTRIES = 5;
	// Task Type
	public static final int TASK_LOGIN = 0;
	public static final int TASK_SEND_MESSAGE = 1;
	public static final int TASK_SEND_FILE = 2;
	public static final int TASK_QUIT = 3;
	public static final int TASK_LOGIN_BACK = 4;

	public static final int CMD_ID_LOGIN = 5;
	public static final int CMD_ID_SEND_MESSAGE = 6;
	public static final int CMD_ID_SEND_FILE = 7;
	
	public static final int CMD_ID_QUIT = 8;
	public static final int CMD_ID_FINISH = 9;
	public static final int CMD_ID_FEED_BACK = 10;

	public int mTaskType = -1;
	public String mMsgContent;
	public String mTargetAddress;

	private static DatagramSocket mServerSocket = null;
	private static DatagramPacket mPacket = null;
	private static int BYTE_LENGTH = 255;

	private static TaskController mTask;
	private Thread mThread;
	private WimCmd mCmd;
	private ArrayList<User> mUserList;
	private ArrayList<String> mIpList = new ArrayList<String>();
	private ArrayList<EventListener> mListener = new ArrayList<EventListener>();
	private ArrayList<EventListener> mMsgListener = new ArrayList<EventListener>();
	// private RefreshUser mRefreshListThread;
	private ClientThread mClient;
	private ServerThread mServer;

	private WimNotificationManager mWm;
	private boolean mEnable = true;
	private boolean mIsBroadcast = false;

	private TaskController() {
		// mThread = new Thread(this);
	}

	public static TaskController getInstance() {
		if (mTask == null) {
			mTask = new TaskController();
		}

		return mTask;
	}

	public void startTask(Context context) {
		mWm = new WimNotificationManager(context);
		/*
		 * if (!mThread.isAlive()) { mThread.start(); }
		 */
		this.startTask();
	}

	public void stopTask() {

		if (mServerSocket != null && !mServerSocket.isClosed()) {
			if (DEBUG) {
				Log.d(TAG, "stopTask:close serverSocket");
			}
			mServerSocket.close();
		}
		/*
		 * if(mServer.isAlive()) { mServer. }
		 */
		/*
		 * try { mServer.stop(); } catch (Exception e) {
		 * mWm.handleError(R.string.socket_io_error); } mClient.stop();
		 */
		// mThread.
		mThread.stop();
	}

	public void stopAllThread() {
		mTaskType = TASK_QUIT;
		// this.mMsgContent = "QUIT:";
		this.mIsBroadcast = false;
		this.mTargetAddress = IMUtil.getHostIPAddress();
		sendMessage(IMUtil.sendQuitMsg());
		mIpList.clear();
		// mThread.destroy();

	}

	public void setTaskType(int type) {
		mTaskType = type;
	}

	/**
	 * 
	 * @param cmd
	 */
	public void handleCommand(WimCmd cmd) {
		if (DEBUG) {
			Log.d(TAG, "issCmd:" + cmd);
		}
		switch (cmd.getCmd()) {
		case CMD_ID_LOGIN:
			// mEnable = false;
			receiveLogin(cmd);
			mEnable = true;
			startServerReceive();
			break;
		case CMD_ID_SEND_MESSAGE:
			if (DEBUG) {
				Log.d(TAG, "handleMesage");
			}
			receiveMessage(cmd);
			mEnable = true;
			startServerReceive();
			break;
		case CMD_ID_SEND_FILE:
		case CMD_ID_FEED_BACK:
			//cmd.getMessageIp();
			receiveFeedBack(cmd);
			mEnable = true;
			startServerReceive();
			break;
		case CMD_ID_QUIT:
			if (DEBUG) {
				Log.d(TAG, "issueCMD:" + CMD_ID_QUIT);
			}
			receiveQuit(cmd);
			
			// mClient.stop();
			break;

		}
	}

	private void receiveQuit(WimCmd cmd) {
		String fromIp = cmd.getFromIpAddress();
		if (IMUtil.getHostIPAddress().equals(fromIp)) {
			mServerSocket.close();
			mServerSocket = null;
		} else {
			if (mIpList.contains(fromIp)) {
				mIpList.remove(fromIp);
				
			}
		}
	}

	private void receiveMessage(WimCmd cmd) {
		if (DEBUG) {
			Log.d(TAG, "@@@@handleMessage:" + cmd.getMessage());
		}
		notifyUser(cmd.getMessage());
	}

	private void notifyUser(String message) {
		ArrayList<EventListener> list = (ArrayList<EventListener>) mListener.clone();
		for (EventListener el:list) {
			el.newMessage(message);
		}
	}

	/**
	 * 1.��255.255.255.255�㲥�������Լ���½ 2.��Ϊ�������˼���22581
	 * 
	 * @throws SocketException
	 */
	private void openServer() {
		if (DEBUG) {
			Log.d(TAG, "openServer");
		}
		try {
			if (mServerSocket == null) {
				mServerSocket = new DatagramSocket(IMUtil.IM_SOCKET_PORT);
				mServerSocket.setSoTimeout(0);
			}

			// mServerSocket.set
			mUserList = new ArrayList<User>();
			startServerReceive();
		} catch (SocketException e) {
			mWm.handleError(R.string.socket_io_error);
			e.printStackTrace();
		}

	}

	private void startServerReceive() {
		if (DEBUG) {
			Log.d(TAG, "startServerReceive:BYTE_LENGTH:" + BYTE_LENGTH);
		}
		DatagramPacket receivePacket = new DatagramPacket(
				new byte[BYTE_LENGTH], BYTE_LENGTH);
		if (mEnable) {
			try {
				if (mServerSocket.isClosed()) {
					openServer();
				}
				if (DEBUG) {
					Log.d(TAG, "startServerReceive");
				}
				// receivePacket.set
				mServerSocket.receive(receivePacket);
				if (DEBUG) {
					Log.d(TAG, "@@@Server received a packet");
				}
				mEnable = false;
				mCmd = new WimCmd(receivePacket);
				handleCommand(mCmd);
			} catch (IOException e) {
				mWm.handleError(R.string.socket_io_error);
				e.printStackTrace();
			}

		}
	}

	public void registerObserver(EventListener el) {
		
		if (!mListener.contains(el)) {
			Log.d(TAG,"registerONbserver:");
			mListener.add(el);
		}
	}

	public void unregisterObserver(EventListener el) {
		if (mListener.contains(el)) {
			mListener.remove(el);
		}
	}
	/**
	 * notify all register
	 */
	private void broadcastObserver(String userIp,boolean isLine) {
		if (DEBUG) {
			Log.d(TAG, "broadcastObserver:");
		}
		ArrayList<EventListener> tmp = (ArrayList<EventListener>) mListener
				.clone();
		if (DEBUG) {
			Log.d(TAG,"broadcastObser:size:"+tmp.size());
		}
		if (isLine) {
			for (EventListener l : tmp) {
				if (DEBUG) {
					Log.d(TAG, "addUser:");
				}
				l.addUser(userIp);
			}
		} else {
			for (EventListener rmL : tmp) {
				rmL.removeUser(userIp);
			}
		}
	}

	private void receiveLogin(WimCmd cmd) {
		if (DEBUG) {
			Log.d(TAG, "addOnLineFriend");
		}
		User user = new User();
		String ip = mCmd.getFromIpAddress();
		user.userIp = ip;
		if (DEBUG) {
			Log.d(TAG, "@@@userIp:" + user.userIp);
		}
		if (!ip.equals(IMUtil.getHostIPAddress())) {
			if (!mIpList.contains(ip)) {
				this.mIsBroadcast = false;
				this.mTargetAddress = ip;
				//this.mMsgContent = IMUtil;
				this.sendMessage(IMUtil.feedBackLogin(ip));
			}
		}
			if (!mIpList.contains(ip)) {
				mIpList.add(ip);
				broadcastObserver(ip,true);
			}
	}
	
	public void receiveFeedBack(WimCmd cmd) {
		String fromIp = cmd.getFromIpAddress();
		User u = new User();
		u.userIp = fromIp;
		if (!mIpList.contains(fromIp)) {
			mIpList.add(fromIp);
			broadcastObserver(fromIp,false);
		}
	}

	public void removeUser(User user) {
		mUserList.remove(user);
	}

	private void sendMessage(String msg) {
		InetAddress targetAddress = null;
		/*if (mIsBroadcast) {
			mTargetAddress = IMUtil.MULTICAST_ADDRESS;
		}
		if (mTargetAddress == null) {
			return;
		}*/
		try {
			String args [] = msg.split(IMUtil.SPLIT);
			targetAddress = InetAddress.getByName(args[1]);
		} catch (UnknownHostException e2) {
			// e2.printStackTrace();
			mWm.handleError(R.string.unknow_host);
		}
		// byte []byteTosend = message.getBytes();
		DatagramSocket socket = null;
		try {
			socket = new DatagramSocket();
		} catch (SocketException e1) {
			e1.printStackTrace();
		}
		try {
			socket.setSoTimeout(TIMEOUT);
		} catch (SocketException e1) {
			e1.printStackTrace();
		}
		byte[] bytesToSend = null;
/*		if (mIsBroadcast) {
			//String msg = IMUtil.sendLoginMessage();
			if (DEBUG) {
				Log.d(TAG, "broadcast message:" + msg);
			}
			bytesToSend = msg.getBytes();

		} else {
			if (mTaskType == TASK_QUIT) {
				bytesToSend = IMUtil.quitMessage().getBytes();
			} else {
				bytesToSend = IMUtil.sendMessage(mMsgContent).getBytes();
			}

		}*/
		// BYTE_LENGTH = bytesToSend.length;
		bytesToSend = msg.getBytes();
		if (DEBUG) {
			Log.d(TAG, "sendPacket:" + new String(bytesToSend));
			Log.d(TAG, "sendPacketLength:" + BYTE_LENGTH);
		}
		DatagramPacket sendPacket = new DatagramPacket(bytesToSend,
				bytesToSend.length, targetAddress, IMUtil.IM_SOCKET_PORT);
		// DatagramPacket receivePacket = new DatagramPacket(new
		// byte[byteTosend.length],byteTosend.length);
		// int tries = 0;
		// boolean receivedResponse = false;
		try {
			socket.send(sendPacket);

		} catch (IOException e) {
			mWm.handleError(R.string.socket_io_error);
			e.printStackTrace();
		}
		socket.close();
	}
	public String getIpList() {
		StringBuilder sb = new StringBuilder();
		for (String ip: mIpList) {
			sb.append(ip);
			sb.append(IMUtil.SPLIT);
		}
		return sb.toString();
	}
	/*
	 * LOGIN:TIP:MIP:CONTENT
	 */
	private class WimCmd {
		//

		private DatagramPacket cmdPacket;
		String[] args;
		String cmd = null;

		public WimCmd(DatagramPacket packet) {
			cmdPacket = packet;

			String infoStream = new String(packet.getData());
			if (DEBUG) {
				Log.d(TAG, "infoStream:" + infoStream);
			}
			args = infoStream.split(":");
		}

		public int getCmd() {
			cmd = args[0];
			if (DEBUG) {
				Log.d(TAG, "getCmd:" + cmd);
			}
			if (cmd.equals(IMUtil.LOGIN)) {
				return CMD_ID_LOGIN;
			} else if (cmd.equals(IMUtil.MESSAGE)) {
				return CMD_ID_SEND_MESSAGE;
			} else if (cmd.equals(IMUtil.QUIT)) {
				return CMD_ID_QUIT;
			} else if (cmd.equals(IMUtil.FEEDBACK)) {
				return CMD_ID_FEED_BACK;
			}
			return -1;
		}

		public String getTargetIp() {
			if (args.length >= 2) {
				return args[1];
			} else {
				return "";
			}

		}
		public String getMessage() {
			StringBuilder sb = new StringBuilder();
			if (args.length >= 4) {
				sb.append(args[0]);
				sb.append(IMUtil.SPLIT);
				sb.append(args[1]);
				sb.append(IMUtil.SPLIT);
				sb.append(args[2]);
				sb.append(IMUtil.SPLIT);
				sb.append(args[3]);
				sb.append(IMUtil.SPLIT);
				return sb.toString();
			}
			return null;
		}
		public String getMsgContent() {
			if (args.length >=4) {
				return args[3];
			}
			return "";
			
		}

		public String getFromIpAddress() {
			return args[2];
		}
		public String showMessage() {
			return args[2]+":"+args[1];
		}
	}

	private void executeTask(int taskType) {
		if (DEBUG) {
			Log.d(TAG, "ececuteTask:taskType:" + taskType);
		}
		switch (taskType) {
		case TASK_LOGIN:
			mEnable = true;
			mServer.start();
			if (DEBUG) {
				Log.d(TAG, "executeTask:startClient:");
			}
			mClient.start();
			break;
		case TASK_SEND_MESSAGE:
			// this.mIsBroadcast = true;
			mClient.start();
			break;
		case TASK_SEND_FILE:
		case TASK_QUIT:
			
		}
	}

	public void startTask() {
		Log.d(TAG, "start task");
		//mIpList.clear();
		
		mClient = new ClientThread();
		mServer = new ServerThread();
		executeTask(mTaskType);
		Log.d(TAG, "main Thread stop");
	}
	
	public class ServerThread extends Thread {

		@Override
		public void run() {
			super.run();
			switch (mTaskType) {
			case TASK_LOGIN:
			case TASK_SEND_MESSAGE:
			case TASK_SEND_FILE:
			case TASK_QUIT:
				openServer();
				break;
			}
			if (DEBUG) {
				Log.d(TAG, "Server Thread finish task");
			}
		}

	}

	public class ClientThread extends Thread {

		@Override
		public void run() {
			super.run();
			switch (mTaskType) {
			case TASK_LOGIN:
				//mIsBroadcast = true;
				sendMessage(IMUtil.sendLoginMessage());
				break;
			case TASK_SEND_MESSAGE:
				//mIsBroadcast = false;
				sendMessage(mMsgContent);
				break;
			case TASK_SEND_FILE:
			case TASK_QUIT:
				sendMessage(IMUtil.sendQuitMsg());
				break;
			}
			if (DEBUG) {
				Log.d(TAG, "Client Thread finish task");
			}
		}

	}
}
